﻿using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace WenAntdUI.License;
public static class HardwareInfo
{
    public static string GenerateSerialNumber()
    {
        var cpuId = GetCpuId();
        var diskId = GetDiskId();
        var hash = SHA256.HashData(Encoding.UTF8.GetBytes(cpuId + diskId));
        return BitConverter.ToString(hash).Replace("-", "")[..32];
    }

    #region CPUID 获取实现
    private static string GetCpuId()
    {
        try
        {
            int[] cpuInfo = new int[4];
            ExecuteCpuid(1, cpuInfo); // 获取基础 CPU 信息
            return $"{cpuInfo[3]:X8}{cpuInfo[0]:X8}"; // EDX + EAX
        }
        catch
        {
            return "00000000";
        }
    }

    // 通过动态汇编代码执行 CPUID 指令
    private static void ExecuteCpuid(int function, int[] result)
    {
        if (result == null || result.Length < 4)
            throw new ArgumentException("Result buffer must have 4 elements");

        // 动态生成机器码（支持 x86/x64）
        byte[] code = nint.Size == 4 ?
            new byte[] { 0x53, 0x57, 0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x7C, 0x24, 0x10, 0x0F, 0xA2, 0x89, 0x07, 0x89, 0x5F, 0x04, 0x89, 0x4F, 0x08, 0x89, 0x57, 0x0C, 0x5F, 0x5B, 0xC3 } :
            new byte[] { 0x53, 0x49, 0x89, 0xD0, 0x89, 0xC8, 0x0F, 0xA2, 0x41, 0x89, 0x00, 0x41, 0x89, 0x58, 0x04, 0x41, 0x89, 0x48, 0x08, 0x41, 0x89, 0x50, 0x0C, 0x5B, 0xC3 };

        nint ptr = Marshal.AllocHGlobal(code.Length);
        Marshal.Copy(code, 0, ptr, code.Length);

        // 设置内存可执行
        VirtualProtect(ptr, (nuint)code.Length, 0x40, out _);

        try
        {
            var cpuidDelegate = Marshal.GetDelegateForFunctionPointer<CpuIdDelegate>(ptr);
            cpuidDelegate(function, result);
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate void CpuIdDelegate(int function, int[] result);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool VirtualProtect(nint lpAddress, nuint dwSize, uint flNewProtect, out uint lpflOldProtect);
    #endregion

    #region 硬盘序列号获取（保持不变）
    private static string GetDiskId()
    {
        const string devicePath = @"\\.\PhysicalDrive0";
        nint hDevice = CreateFile(
            devicePath,
            0,
            FileShare.Read | FileShare.Write,
            nint.Zero,
            FileMode.Open,
            0,
            nint.Zero);

        if (hDevice == INVALID_HANDLE_VALUE)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        try
        {
            var query = new STORAGE_PROPERTY_QUERY
            {
                PropertyId = STORAGE_PROPERTY_ID.StorageDeviceProperty,
                QueryType = STORAGE_QUERY_TYPE.PropertyStandardQuery
            };

            int descriptorSize = Marshal.SizeOf(typeof(STORAGE_DEVICE_DESCRIPTOR));
            byte[] buffer = new byte[descriptorSize + 512]; // 扩大缓冲区

            if (!DeviceIoControl(
                hDevice,
                IOCTL_STORAGE_QUERY_PROPERTY,
                ref query,
                Marshal.SizeOf(query),
                buffer,
                buffer.Length,
                out _,
                nint.Zero))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            var descriptor = (STORAGE_DEVICE_DESCRIPTOR)Marshal.PtrToStructure(
                Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0),
                typeof(STORAGE_DEVICE_DESCRIPTOR));

            if (descriptor.SerialNumberOffset == 0)
                return "UNKNOWN";

            return Encoding.ASCII.GetString(
                buffer,
                (int)descriptor.SerialNumberOffset,
                Array.IndexOf(buffer, (byte)0, (int)descriptor.SerialNumberOffset) - (int)descriptor.SerialNumberOffset);
        }
        catch
        {
            return "UNKNOWN";
        }
        finally
        {
            CloseHandle(hDevice);
        }
    }
    #endregion

    #region P/Invoke 声明

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern nint CreateFile(
        string lpFileName,
        uint dwDesiredAccess,
        FileShare dwShareMode,
        nint lpSecurityAttributes,
        FileMode dwCreationDisposition,
        uint dwFlagsAndAttributes,
        nint hTemplateFile);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool DeviceIoControl(
        nint hDevice,
        uint dwIoControlCode,
        ref STORAGE_PROPERTY_QUERY lpInBuffer,
        int nInBufferSize,
        byte[] lpOutBuffer,
        int nOutBufferSize,
        out int lpBytesReturned,
        nint lpOverlapped);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(nint hObject);

    [DllImport("kernel32.dll")]
    private static extern void Cpuid(int function, int[] result);

    private const uint FILE_DEVICE_MASS_STORAGE = 0x0000002d;
    private const uint IOCTL_STORAGE_QUERY_PROPERTY =
        FILE_DEVICE_MASS_STORAGE << 16 | 0x0500 << 2 | 0 | 0x0400;

    private static readonly nint INVALID_HANDLE_VALUE = new nint(-1);

    private enum STORAGE_PROPERTY_ID
    {
        StorageDeviceProperty = 0
    }

    private enum STORAGE_QUERY_TYPE
    {
        PropertyStandardQuery = 0
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct STORAGE_PROPERTY_QUERY
    {
        public STORAGE_PROPERTY_ID PropertyId;
        public STORAGE_QUERY_TYPE QueryType;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] AdditionalParameters;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct STORAGE_DEVICE_DESCRIPTOR
    {
        public uint Version;
        public uint Size;
        public byte DeviceType;
        public byte DeviceTypeModifier;
        [MarshalAs(UnmanagedType.U1)]
        public bool RemovableMedia;
        [MarshalAs(UnmanagedType.U1)]
        public bool CommandQueueing;
        public uint VendorIdOffset;
        public uint ProductIdOffset;
        public uint ProductRevisionOffset;
        public uint SerialNumberOffset;
        public uint BusType;
        public uint RawPropertiesLength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
        public byte[] RawDeviceProperties;
    }

    #endregion
}